#ifndef __TASK_POOL__
#define __TASK_POOL__

#include <queue>
#include <vector>
#include <atomic>
#include <condition_variable>
#include <thread>
#include <iostream>
#include <memory>
#include "EvenTask.h"

using namespace std;



class TASKPOOL
{
    #define GROW_STEP_NUMS 10
    #define MAX_THREAD_NUMS 50
    static TASKPOOL* m_pInstance;
public:
    static TASKPOOL* getInstance()
    {
        if(!m_pInstance)
        {
            m_pInstance = new TASKPOOL(true);
        }

        return m_pInstance;
    }

public:
    TASKPOOL() = delete;
    TASKPOOL(bool isAuto = false) : m_isAutoPool(isAuto)
    {
        m_run = true;
        if(m_isAutoPool)
        {
            m_recoveryThr.reset(new thread( &TASKPOOL::recoveryTask, this ));
        }
    }

    ~TASKPOOL()
    {
         m_run = false;

        m_qcond.notify_all();
        for(thread& i : m_pool)
        {
            if(i.joinable())
                i.join();   //wait pool exit
        }

        //destory recovery thr
        if(m_isAutoPool)
        {           
            m_recoveryThr->join();
        }
    }

public:
    bool addPool(unsigned short nums)
    {
        if(m_isAutoPool)
            return false;   //if auto_grow should get locker protect

        return addThr(nums);
    }

    bool commitTask(const shared_ptr<EvenTask> &task)
    {
        if( !m_run )
        {
            //is recoverying ,can't add task
            return false;
        }

        {
            unique_lock<mutex> qlk(m_qlock);
            m_task.emplace(task); 
            ++m_waitingTaskNums   ;
        }

        if(m_isAutoPool)
        {
            lock_guard<mutex> lk(m_poollock);
            if(m_idleThrNums < 1 && (m_pool.size() < MAX_THREAD_NUMS))
            {
                addThr(GROW_STEP_NUMS);
            }
        }
        
        m_qcond.notify_one();     
        return true;
    }

#if 1
    bool isBusy()
    {
        lock_guard<mutex> lk(m_poollock);
        return ( (m_idleThrNums < m_pool.size()) && (m_waitingTaskNums > 0));
    }
#endif

private:
    bool addThr(unsigned short nums)
    {
       for( ; m_pool.size() < MAX_THREAD_NUMS && nums > 0; --nums)
       {
           m_pool.emplace_back([this](){
               while(m_run)
               {
                   shared_ptr<EvenTask> Task{nullptr};
                   {
                       unique_lock<mutex> qlk(m_qlock);
                       while( m_run && m_task.empty())
                       {
                           m_qcond.wait(qlk);
                       }
                       if( !m_run )
                       {
                           cout << "byebye -- " << this_thread::get_id() <<endl;
                           return;
                       }

                       Task = move(m_task.front());
                       m_task.pop();
                       --m_waitingTaskNums;
                   }
                   m_idleThrNums--;
                   Task->task();
                   m_idleThrNums++;
               }
               cout << "byebye -- " << this_thread::get_id() <<endl;
           });

           m_idleThrNums++;      
       }

       return true;
    }

    void recoveryTask()
    {
        //this_thread::sleep_for(std::chrono::milliseconds(650));
        while(m_run)
        {
            //cout << "recoveryTask" << endl;
            {
                lock_guard<mutex> lk(m_poollock);
                if( !m_pool.empty() && (m_idleThrNums == m_pool.size()))
                {
                    //have no running thr
                    //recovery pool resource
                    
                    if(m_waitingTaskNums == 0)
                    {
                        #if 1
                        m_run =false;
                        m_qcond.notify_all();
                        for(thread& i : m_pool)
                        {
                            if(i.joinable())
                                i.join();   //wait exit
                        }
                        
                        m_pool.clear();
                        m_idleThrNums = 0;
                        m_run =true;
                        #endif 
                    }

                    cout << "---------------recvoernenedn----end" << endl;
                }
            }


            //if pool need destory,may need wait a half of second time
            this_thread::sleep_for(std::chrono::milliseconds(1000));
        } 
    }

private:
    //pool running enable flag
    atomic_bool m_run{false};
    //queue locker
    mutex m_qlock;
    //pool lock
    mutex m_poollock;
    //
    condition_variable m_qcond;
    //
    vector<thread> m_pool{};
    //
    queue<shared_ptr<EvenTask>> m_task{};
    //idle thr
    atomic_short m_idleThrNums{0};
    //task wait
    atomic_int m_waitingTaskNums{0};

private:
    //grown /delete auto flag
    bool m_isAutoPool{false};
    unique_ptr<thread> m_recoveryThr{nullptr};

};

TASKPOOL* TASKPOOL::m_pInstance = NULL;

#endif